home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#49 (Oct 89)
/
SC #49.sit
/
Lazy-Lists.Lisp
next >
Wrap
Lisp/Scheme
|
1989-05-10
|
6KB
|
154 lines
;;;This file contains all kinds of wonderful "stream" stuff from Abelson and
;;;Sussman. The problem is that "stream" means something else within the
;;;context of Common Lisp and therefore within Pearl Lisp, so I call 'em
;;;"lazy-lists," which to me makes more sense anyway.
;;Define the empty lazy list:
(defconstant the-empty-lazy-list '())
;;How do we know if a lazy-list is empty?
(defun empty-lazy-list-p (lazy-list)
(eq the-empty-lazy-list lazy-list))
;;This function optimizes DELAY so that the function created is only called once.
(defun memoize (fun)
(let ((already-evaled nil) (value nil))
#'(lambda ()
(if already-evaled
value
(prog1
(setf value (funcall fun))
(setf already-evaled t))))))
;;Here is the DELAY macro:
(defmacro delay (thing)
`(memoize #'(lambda () ,thing)))
;;Here is the FORCE function:
(defun force (promise)
(funcall promise))
;;Here is our lazy-list CONStructor:
(defmacro lazy-cons (thing lazy-list)
`(cons ,thing (delay ,lazy-list)))
;;Here are LAZY-CAR and LAZY-CDR:
(defun lazy-car (lazy-list)
(car lazy-list))
(defun lazy-cdr (lazy-list)
(force (cdr lazy-list)))
;;This is to lazy lists what Common Lisp's APPEND is to normal lists.
(defun append-lazy-lists (l1 l2)
(if (empty-lazy-list-p l1)
l2
(lazy-cons (lazy-car l1)
(append-lazy-lists (lazy-cdr l1) l2))))
;;This is a nice, generic accumulation function that takes a combiner function
;;(usually #'+ or #'cons or something like that), an initial value (typically
;;0 or 1 for numeric accumulations or '() for lists) and some lazy-list to
;;apply all of this to.
(defun accumulate (combiner initial-value lazy-list)
(if (empty-lazy-list-p lazy-list)
initial-value
(funcall combiner (lazy-car lazy-list)
(delay (accumulate combiner
initial-value
(lazy-cdr lazy-list))))))
;;This function prevents infinite recursion when accumulating infinite lazy-lists.
(defun interleave (l1 l2)
(if (empty-lazy-list-p l1)
(force l2)
(lazy-cons (lazy-car l1)
(interleave (force l2) (delay (lazy-cdr l1))))))
;;This handy thing uses ACCUMULATE to flatten a lazy-list of lazy-lists.
(defun flatten (lazy-list)
(accumulate #'interleave the-empty-lazy-list lazy-list))
;;This maps some proc across every element of some lazy-list.
(defun lazy-map (proc lazy-list)
(if (empty-lazy-list-p lazy-list)
the-empty-lazy-list
(lazy-cons (funcall proc (lazy-car lazy-list))
(lazy-map proc (lazy-cdr lazy-list)))))
;;This returns the lazy-list that contains all items that, when passed to pred,
;;return something non-NIL.
(defun filter (pred lazy-list)
(cond ((empty-lazy-list-p lazy-list) the-empty-lazy-list)
((funcall pred (lazy-car lazy-list))
(lazy-cons (lazy-car lazy-list)
(filter pred (lazy-cdr lazy-list))))
(t (filter pred (lazy-cdr lazy-list)))))
;;This is an awful lot like LAZY-MAP, except that it doesn't accumulate its
;;results, which is a fancy way of saying that you use LAZY-MAP if you need
;;a function result and FOR-EACH if you need side-effects.
(defun for-each (proc lazy-list)
(if (empty-lazy-list-p lazy-list)
'done
(progn (funcall proc (lazy-car lazy-list))
(for-each proc (lazy-cdr lazy-list)))))
;;Flattening the result of lazy-mapping is so useful and so common that there's
;;a whole separate function for it.
(defun flatmap (f s)
(flatten (lazy-map f s)))
;;Sometimes (ok, rarely) it's nice to convert a list to a lazy-list:
(defun lazy-list (list)
(if (null list)
the-empty-lazy-list
(lazy-cons (car list) (lazy-list (cdr list)))))
;;This is the tricky one. The COLLECT macro makes nested mappings a tad easier
;;than they would be otherwise, but this is the most complex macro I've ever
;;had to write. Here goes nothing:
(defmacro collect (result pairs &optional (restriction t))
(let ((vars (mapcar #'car pairs))
(sets (mapcar #'cadr pairs))
(lets (genlets pairs)))
`(lazy-map #'(lambda (tuple)
(let ,lets
,result))
(filter #'(lambda (tuple)
(let ,lets
,restriction))
,(genmaps vars sets)))))
;;Given a list of pairs, this creates a let body based on tuple.
(defun genlets (pairs)
(do ((i (1- (length pairs)) (1- i))
(result '() (cons (cons (car (nth i pairs)) (list (list 'nth i 'tuple))) result)))
((< i 0) result)))
;;This beast generates the flatmap/lazy-map sequence for the vars and sets.
(defun genmaps (vars sets)
(labels ((genmaps-1 (vars sets depth)
(if (null (cdr sets))
`(lazy-map #'(lambda (,(car (last vars)))
(list ,@vars))
,(car sets))
`(flatmap #'(lambda (,(nth depth vars))
,(genmaps-1 vars (cdr sets) (1+ depth)))
,(car sets)))))
(genmaps-1 vars sets 0)))
(defconstant ones (lazy-cons 1 ones))
(defun add-lazy-lists (l1 l2)
(cond ((empty-lazy-list-p l1) l2)
((empty-lazy-list-p l2) l1)
(t
(lazy-cons (+ (lazy-car l1) (lazy-car l2))
(add-lazy-lists (lazy-cdr l1) (lazy-cdr l2))))))
(defconstant integers (lazy-cons 1 (add-lazy-lists ones integers)))
(defun scale-lazy-list (c lazy-list)
(lazy-map #'(lambda (x) (* x c)) lazy-list))